﻿using System;
using System.ComponentModel;
using System.Linq;
using System.Windows.Markup;

namespace Red.Controls.Extensions
{
    /// <summary>
    /// Allows the user to bind a combobox to an enum type
    /// </summary>
    /// <example>
    /// In C#:
    /// enum Status
    /// {
    ///     [Description("Available.")]
    ///     Available,
    ///     [Description("Not here right now.")]
    ///     Away,
    ///     [Description("I don't have time right now.")]
    ///     Busy
    /// }
    /// 
    /// In xaml:
    /// <ComboBox 
    ///     ItemsSource="{Binding Source={xx:Enumeration {x:Type yy:Status}}}"
    ///     DisplayMemberPath="Description"
    ///     SelectedValue="{Binding CurrentStatus}"
    ///     SelectedValuePath="Value" />
    ///     Where xx is the xmlns alias for the namespace containing EnumerationExtension and yy the alias for the namespace that contains Status
    /// </example>
    /// <remarks>
    /// Taken directly from here http://stackoverflow.com/a/4398752/1683284
    /// Virtually unchanged from source
    /// </remarks>
    public class EnumerationExtension : MarkupExtension
    {
        private Type _enumType;

        public EnumerationExtension( Type enumType )
        {
            if ( enumType == null )
                throw new ArgumentNullException( "enumType" );

            EnumType = enumType;
        }

        public Type EnumType
        {
            get { return _enumType; }
            private set
            {
                if ( _enumType == value )
                    return;

                var enumType = Nullable.GetUnderlyingType( value ) ?? value;

                if ( enumType.IsEnum == false )
                    throw new ArgumentException( "Type must be an Enum." );

                _enumType = value;
            }
        }

        public override object ProvideValue( IServiceProvider serviceProvider )
        {
            var enumValues = Enum.GetValues( EnumType );

            return (
              from object enumValue in enumValues
              select new EnumerationMember
              {
                  Value = enumValue,
                  Description = GetDescription( enumValue )
              } ).ToArray();
        }

        private string GetDescription( object enumValue )
        {
            var descriptionAttribute = EnumType
              .GetField( enumValue.ToString() )
              .GetCustomAttributes( typeof( DescriptionAttribute ), false )
              .FirstOrDefault() as DescriptionAttribute;


            return descriptionAttribute != null
              ? descriptionAttribute.Description
              : enumValue.ToString();
        }

        public class EnumerationMember
        {
            public string Description { get; set; }
            public object Value { get; set; }
        }
    }
}
